////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
//#include "stdafx.h"

#include "Utils.h"
#include "os.h"
#ifdef WIN32
#include "Windows.h"
#endif
#include "Common/Logger.h"
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;

////////////////////////////////////////////////////////////////////////////////////////////////////

// ----------------------------------------------------------------------------------------------------
/// @brief Returns the mesh of the node according to the node type
// ----------------------------------------------------------------------------------------------------
IMesh* GetMesh(ISceneNode* node)
{
	if(!node)
	{
		return 0;
	}
	
	// get node mesh
	IMesh* mesh = 0;
	
	switch(node->getType())
	{
	#ifdef _IRR_COMPILE_WITH_COLLADA_BINARY_LOADER_
		case ESNT_COLLADA_SKIN_MESH:
		case ESNT_COLLADA_MODULAR_SKIN_MESH:
			mesh = ((collada::CSkinnedMeshSceneNode*)node)->getMesh();
		break;

		case ESNT_COLLADA_MESH:
	#endif
		case ESNT_MESH:
		//case ESNT_BATCH_SCENE_NODE:
		//case ESNT_BATCH_GRID_SCENE_NODE:
			mesh = ((IMeshSceneNode*)node)->getMesh();
		break;		
	}

	return mesh;
}

// ----------------------------------------------------------------------------------------------------
/// @brief Modifies the brightness(0-255) of a mesh through the vertex colors
// ----------------------------------------------------------------------------------------------------
//void SetVertexColor(ISceneNode* node, const irr::video::SColor& color)
//{
//	if(!node)
//	{
//		return;
//	}
//
//	irr::video::SColor vertexColor = color;
//	
//	IMesh* mesh = GetMesh(node);
//	
//	if(mesh && node->getMaterialCount() > 0)
//	{
//		s32 type = node->getMaterial(0).MaterialType;
//		if(type == EMT_SOLID || type == EMT_TRANSPARENT_VERTEX_ALPHA || type == EMT_TRANSPARENT_ALPHA_CHANNEL || type == EMT_REFLECTION_2_LAYER)
//		{
//			u32 cnt = mesh->getMeshBufferCount();
//			for (u32 i = 0; i < cnt; ++i)
//			{
//				IMeshBuffer* buf = mesh->getMeshBuffer(i);
//				if(buf->getVertexType() == EVT_COMPONENT_ARRAYS)
//				{
//					SMaterial& mat = node->getMaterial(i);
//					irr::u8 alpha = mat.getDiffuseColor().A;
//					vertexColor.A = alpha;
//					mat.setDiffuseColor(vertexColor);
//				
//
//					S3DVertexComponentArrays *pVertices = reinterpret_cast<S3DVertexComponentArrays*>(buf->getVertices());
//					// remove color channel in order to force copy the diffuse color into the color channel
//					pVertices->Color0 = 0;
//				}
//			}
//		}
//	}
//	
//	core::list<ISceneNode*>::ConstIterator it = node->getChildren().begin();
//	for (; it != node->getChildren().end();)
//	{
//		ISceneNode *n = (*it);
//		it++;
//		SetVertexColor(n, color);
//	}
//}

//void SetVertexAlpha(ISceneNode* node, int alpha)
//{
//	if(!node)
//	{
//		return;
//	}
//	
//	IMesh* mesh = GetMesh(node);
//	
//	if(mesh && node->getMaterialCount() > 0)
//	{
//		s32 type = node->getMaterial(0).getMaterialType();
//		if(type == EMT_SOLID || type == EMT_TRANSPARENT_VERTEX_ALPHA || type == EMT_TRANSPARENT_ALPHA_CHANNEL || type == EMT_REFLECTION_2_LAYER)
//		{
//			u32 cnt = mesh->getMeshBufferCount();
//			for (u32 i = 0; i < cnt; ++i)
//			{
//				IMeshBuffer* buf = mesh->getMeshBuffer(i);
//				if(buf->getVertexType() == EVT_COMPONENT_ARRAYS)
//				{
//					SMaterial& mat = node->getMaterial(i);
//					mat.setDiffuseAlpha(alpha);
//				
//
//					S3DVertexComponentArrays *pVertices = reinterpret_cast<S3DVertexComponentArrays*>(buf->getVertices());
//					// remove color channel in order to force copy the diffuse color into the color channel
//					pVertices->Color0 = 0;
//				}
//			}
//		}
//	}
//	
//	core::list<ISceneNode*>::ConstIterator it = node->getChildren().begin();
//	for (; it != node->getChildren().end();)
//	{
//		ISceneNode *n = (*it);
//		it++;
//		SetVertexAlpha(n, alpha);
//	}
//}
//
//
//void SetMaterialFlag(ISceneNode *Node, E_MATERIAL_FLAG flag, bool newvalue)
//{
//	if (Node->getMaterialCount() > 0)
//		Node->setMaterialFlag(flag, newvalue);
//	
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		SetMaterialFlag( (*it), flag, newvalue);
//	}
//}


//void SetMaterialBlendEq(ISceneNode *Node, E_BLEND_EQ eq, bool updateChildren)
//{
//	if (Node->getMaterialCount() > 0)
//		Node->setMaterialBlendEquation(eq);
//
//	if(!updateChildren)
//		return;
//	
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		SetMaterialBlendEq( (*it), eq);
//	}
//}

//void SetMaterialTexture(ISceneNode *Node, int texID, irr::video::ITexture* tex)
//{
//	if (Node->getMaterialCount() > 0)
//		Node->setMaterialTexture(texID, tex);
//	
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		SetMaterialTexture( (*it), texID, tex);
//	}
//}
//
//void SetMaterialColorMaskRGBA(ISceneNode *Node, unsigned int mask)
//{
//	if (Node->getMaterialCount() > 0)
//		Node->setMaterialColorMaskRGBA(mask);
//
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		SetMaterialColorMaskRGBA( (*it), mask);
//	}
//}
//
//void SetMaterialDepthFunc(ISceneNode *Node, E_COMPARE_FUNC f)
//{
//	if (Node->getMaterialCount() > 0)
//		Node->setMaterialCompareFunc(f);
//
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		SetMaterialDepthFunc( (*it), f);
//	}
//}
//
//void SetMaterialAmbientColor(ISceneNode *Node, SColor color)
//{
//	if (Node->getMaterialCount() > 0)
//		Node->setMaterialAmbientColor(color);
//
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		SetMaterialAmbientColor( (*it), color);
//	}
//}
//
//void SetMaterialDiffuseColor(ISceneNode *Node, SColor color)
//{
//	if (Node->getMaterialCount() > 0)
//		Node->setMaterialDiffuseColor(color);
//
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		SetMaterialDiffuseColor( (*it), color);
//	}
//}
//
//void FindMaterialTypes(ISceneNode *Node, std::vector<NodeMatTypeInfo*>& nodeMatTypes)
//{
//	int matCount = Node->getMaterialCount();
//	if(matCount>0)
//	{
//		NodeMatTypeInfo* info = new NodeMatTypeInfo();
//		info->Node = Node;
//		info->MaterialCount = matCount;
//
//		for (int i=0; i<matCount; i++)
//		{	
//			irr::video::E_MATERIAL_TYPE matType = Node->getMaterial(i).getMaterialType();
//			info->MaterialTypes.push_back(matType);
//		}
//
//		nodeMatTypes.push_back(info);
//	}
//	
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		FindMaterialTypes((*it), nodeMatTypes);
//	}	
//}
//
//void SetMaterialTypes(const std::vector<NodeMatTypeInfo*>& nodeMatTypes)
//{
//	for(int i=0, is=nodeMatTypes.size(); i<is; ++i)
//	{
//		NodeMatTypeInfo* info = nodeMatTypes[i];
//		for(int j=0; j<info->MaterialCount; j++)
//		{
//			info->Node->getMaterial(j).setMaterialType(info->MaterialTypes[j]);
//		}
//	}
//}
//
//void ReplaceMaterialType(ISceneNode *Node, irr::video::E_MATERIAL_TYPE oldType, irr::video::E_MATERIAL_TYPE type)
//{
//	if (Node->getMaterialCount() > 0)
//	{
//		for(int i=0, is=Node->getMaterialCount(); i<is; i++)
//		{
//			if(Node->getMaterial(i).getMaterialType()==oldType)
//				Node->getMaterial(i).setMaterialType(type);
//		}
//	}
//
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		ReplaceMaterialType( (*it), oldType, type);
//	}
//}
//
//void SetMaterialType(ISceneNode *Node, irr::video::E_MATERIAL_TYPE type)
//{
//	if (Node->getMaterialCount() > 0)
//		Node->setMaterialType(type);
//
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		SetMaterialType( (*it), type);
//	}
//}
//
//irr::scene::ISceneNode * SetMaterialTypeByName(irr::scene::ISceneNode *Node, const char* texStr, irr::video::E_MATERIAL_TYPE type)
//{		
//	for(int m=0; m<Node->getMaterialCount(); m++)
//	{
//		irr::video::SMaterial& material = Node->getMaterial(m);
//		irr::video::ITexture* texture=0;
//		texture = material.getTexture(0);
//		if(texture!=0)
//		{
//			irr::core::stringc name = texture->getName();						
//			if(name.find(texStr)>=0)
//			{
//				Node->setMaterialType(type);				
//				return Node;
//			}
//		}
//	}
//
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		irr::scene::ISceneNode * result = SetMaterialTypeByName( (*it), texStr, type);
//		if(result)
//			return result;
//	}
//
//	return 0;
//}
//
//void SetMaterialAdditiveByTexName(irr::scene::ISceneNode *Node, const char* texStr)
//{		
//	for(int m=0; m<Node->getMaterialCount(); m++)
//	{
//		irr::video::SMaterial& material = Node->getMaterial(m);
//		irr::video::ITexture* texture=0;
//		texture = material.getTexture(0);
//		if(texture!=0)
//		{
//			irr::core::stringc name = texture->getName();						
//			if(name.find(texStr)>=0)
//			{
//				Node->setMaterialType(irr::video::EMT_TRANSPARENT_ADD_COLOR);				
//				Node->setRenderingLayer(7);//RL_HIT_EFFECTS
//			}
//		}
//	}
//
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		SetMaterialAdditiveByTexName( (*it), texStr);	
//	}	
//}
//
//void SetMaterialThickness(ISceneNode *Node, float t)
//{
//	if (Node->getMaterialCount() > 0)
//		Node->setMaterialThickness(t);
//
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		SetMaterialThickness( (*it), t);
//	}
//}
//
//void SetNodeRenderingLayer(ISceneNode *Node, int layer)
//{
//	Node->setRenderingLayer(layer);
//
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		SetNodeRenderingLayer( (*it), layer);
//	}
//}

////////////////////////////////////////////////////////////////////////////////////////////////////

void ReplaceBoxAutomaticCullingWithFrustrumBox(ISceneNode* Node)
{
	if(!Node)
	{
		return;
	}

	if (Node->getAutomaticCulling() == EAC_BOX)
	{
		Node->setAutomaticCulling(EAC_FRUSTUM_BOX);
	}
	
	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
	for (; it != Node->getChildren().end(); ++it)
	{
		ReplaceBoxAutomaticCullingWithFrustrumBox((*it));
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void SetAutomaticCulling(ISceneNode* Node, E_CULLING_TYPE cullType)
{
	if(!Node)
		return;

	Node->setAutomaticCulling(cullType);
	
	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
	for (; it != Node->getChildren().end(); ++it)
	{
		SetAutomaticCulling((*it), cullType);
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////

//IMeshSceneNode* GetColladaMeshFromVisualScene(ISceneNode *Node)
//{
//	ESCENE_NODE_TYPE type = Node->getType();
//	if(Node->getType() == ESNT_COLLADA_MESH)
//	{
//		return (IMeshSceneNode*)Node;
//	}
//
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		IMeshSceneNode* ChildNode = GetColladaMeshFromVisualScene(*it);
//
//		if(ChildNode)
//		{
//			return ChildNode;
//		}
//	}
//
//	return 0;
//}

////////////////////////////////////////////////////////////////////////////////////////////////////

//IMeshSceneNode* GetColladaSkinMeshFromVisualScene(ISceneNode *Node)
//{
//	ESCENE_NODE_TYPE type = Node->getType();
//	if(Node->getType() == ESNT_COLLADA_SKIN_MESH)
//	{
//		return (IMeshSceneNode*)Node;
//	}
//
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		IMeshSceneNode* ChildNode = GetColladaSkinMeshFromVisualScene(*it);
//
//		if(ChildNode)
//		{
//			return ChildNode;
//		}
//	}
//
//	return 0;
//}
////////////////////////////////////////////////////////////////////////////////////////////////////

//void GetColladaMeshesListFromVisualScene(ISceneNode *Node, array<CColladaMeshSceneNode*> *meshes)
//{
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		if((*it)->getType() == ESNT_COLLADA_MESH)
//			meshes->push_back((CColladaMeshSceneNode*)(*it));
//		else
//			GetColladaMeshesListFromVisualScene(*it, meshes);
//	}
//}
//
//////////////////////////////////////////////////////////////////////////////////////////////////////
//collada::CCameraSceneNode* GetColladaCameraFromVisualScene(ISceneNode *Node)
//{
//	ESCENE_NODE_TYPE type = Node->getType();
//	if(Node->getType() == ESNT_COLLADA_CAMERA)
//	{
//		return (collada::CCameraSceneNode*)Node;
//	}
//
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		collada::CCameraSceneNode* ChildNode = GetColladaCameraFromVisualScene(*it);
//
//		if(ChildNode)
//		{
//			return ChildNode;
//		}
//	}
//
//	return 0;
//}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//
//collada::CSceneNode* GetColladaNodeFromVisualScene(const char* name , ISceneNode* Node)
//{	
//	if( strcmp(Node->getName() , name) == 0 )
//	{
//		return (collada::CSceneNode*)Node;
//	}
//
//	core::list<ISceneNode*>::ConstIterator it = Node->getChildren().begin();
//	for (; it != Node->getChildren().end(); ++it)
//	{
//		collada::CSceneNode* ChildNode = GetColladaNodeFromVisualScene(name , *it);
//
//		if(ChildNode)
//		{
//			return ChildNode;
//		}
//	}
//
//	return 0;
//}

void UpdateNodeAbsPosition(ISceneNode* node)
{
	node->updateAbsolutePosition();
	return;

	//do NOT use the code below. It will cause bug.
	/*core::array<ISceneNode*> nodeList(15);
	do
	{
		nodeList.push_back(node);
	}
	while ((node = node->getParent()) != NULL);

	for (int i = nodeList.size() - 1; i >= 0; i--)
	{
		nodeList[i]->updateAbsolutePosition();
	}*/
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void GetRotationFromVectorToVector(const vector3df& from, const vector3df& to, matrix4& m)
{
	const float EPSILON = 0.f;

	vector3df	v = from.crossProduct(to);
	const float	e = from.dotProduct(to);

	if(e >= 1.0 - EPSILON) /* "from" almost or equal to "to"-vector? */
	{
		/* return identity */
		m.makeIdentity();
	}
	else if(e <= (-1.0 + EPSILON)) /* "from" almost or equal to negated "to"? */
	{
		vector3df left;

		/* left=CROSS(from, (1,0,0)) */
		left.Z	=	0.f;
		left.Y	=	from.Z;
		left.Z	=	-from.Y;

		if(left.getLengthSQ() < EPSILON) /* was left=CROSS(from,(1,0,0)) a good choice? */
		{
			/* here we now that left = CROSS(from, (1,0,0)) will be a good choice */
			left.X	=	-from.Z;
			left.Y	=	0.f;
			left.Z	=	from.X;
		}

		/* normalize "left" */
		left.normalize();

		vector3df up = left.crossProduct(from);
		/* now we have a coordinate system, i.e., a basis; */
		/* M=(from, up, left), and we want to rotate to: */
		/* N=(-from, up, -left). This is done with the matrix:*/
		/* N*M^T where M^T is the transpose of M */

		const float fxx		=	-from.X * from.X;
		const float fyy		=	-from.Y * from.Y;
		const float fzz		=	-from.Z * from.Z;
		const float fxy		=	-from.X * from.Y;
		const float fxz		=	-from.X * from.Z;
		const float fyz		=	-from.Y * from.Z;

		const float uxx		=	up.X * up.X;
		const float uyy		=	up.Y * up.Y;
		const float uzz		=	up.Z * up.Z;
		const float uxy		=	up.X * up.Y;
		const float uxz		=	up.X * up.Z;
		const float uyz		=	up.Y * up.Z;

		const float lxx		=	-left.X * left.X; 
		const float lyy		=	-left.Y * left.Y; 
		const float lzz		=	-left.Z * left.Z;
		const float lxy		=	-left.X * left.Y;
		const float lxz		=	-left.X * left.Z;
		const float lyz		=	-left.Y * left.Z;

		/* symmetric matrix */
		m(0, 0)		=	fxx + uxx + lxx; 
		m(0, 1)		=	fxy + uxy + lxy; 
		m(0, 2)		=	fxz + uxz + lxz;
		m(1, 0)		=	m(0, 1); 
		m(1, 1)		=	fyy + uyy + lyy; 
		m(1, 2)		=	fyz + uyz + lyz;
		m(2, 0)		=	m(0, 2); 
		m(2, 1)		=	m(1, 2); 
		m(2, 2)		=	fzz + uzz + lzz;
	}	
	else /* the most common case, unless "from"="to", or "from"=-"to" */
	{
		const float h		=	(1.f - e) / v.getLengthSQ();
		const float hvx		=	h * v.X;
		const float hvz		=	h * v.Z;
		const float hvxy	=	hvx * v.Y;
		const float hvxz	=	hvx * v.Z;
		const float hvyz	=	hvz * v.Y;

		m(0, 0)	=	e + hvx * v.X;
		m(0, 1)	=	hvxy - v.Z; 
		m(0, 2)	=	hvxz + v.Y;
		m(1, 0)	=	hvxy + v.Z; 
		m(1, 1)	=	e + h * v.Y * v.Y;
		m(1, 2)	=	hvyz - v.Z;
		m(2, 0)	=	hvxz - v.Y; 
		m(2, 1)	=	hvyz + v.X; 
		m(2, 2)	=	e + hvz * v.Z;
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////

s32 random(int a , int b)
{
	s32 c = (b - a);
	s32 d = 0;

	if (c > 0)
	{
		d = a + irr::os::Randomizer::rand() % c;
	}
	else if (c == 0)
	{
		d = a;
	}

	return d;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

s32 random(int a)
{
	if ( a > 0 )
	{
		return os::Randomizer::rand() % a;
	}

	return 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

f32 Interp(f32 valMin, f32 valMax, f32 stepMin, f32 step, f32 stepMax)
{
   return valMin + ( valMax - valMin ) * ( step - stepMin ) / ( stepMax - stepMin );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

#if defined(_IRR_STATIC_LIB_)

//void* IrrAlloc(size_t size, irr::MemHint hint) 
//{
//#if defined (_IRR_USE_IPHONEOS_DEVICE_) 
//	return new char[size];
//#else
//	//return malloc(size);
//	return new char[size];
//#endif
//}
//
//
//void* IrrAlloc(size_t size, irr::MemHint hint, int block, const char* filename, int line)
//{
//	return IrrAlloc(size, hint);
//}

void IrrFree(void* p)
{
	char *pPointer = static_cast<char*>(p);
#if defined (_IRR_USE_IPHONEOS_DEVICE_) 
	delete[] pPointer;
#else
	//free(p);
	delete[] p;
#endif
}

#endif

#ifdef WIN32

void OSReport ( const char* msg, ... )
{	
	char tmp[1024];
	va_list marker;
	va_start(marker, msg);
	memset(tmp, 0, 1024);
	vsprintf_s(tmp, msg, marker);
	va_end(marker);

	WCHAR wstr[1024];
	mbstowcs(wstr, tmp, 1024);
	OutputDebugStringW(wstr);
	return;
}
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
